package com.ciei.dpagm.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ciei.dpagm.common.enums.*;
import com.ciei.dpagm.common.service.BaseService;
import com.ciei.dpagm.entity.*;
import com.ciei.dpagm.mapper.ParameterMapper;
import com.ciei.dpagm.util.*;
import com.ciei.dpagm.util.DateUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class ParameterService extends BaseService<ParameterMapper, Parameter> {

    @Lazy
    @Autowired
    private ParameterModelService parameterModelService;

    @Lazy
    @Autowired
    private ParameterModuleService parameterModuleService;

    @Autowired
    private RuleSetParameterModuleService ruleSetParameterModuleService;

    @Value("${baseFilePath}")
    private String baseFilePath;

    /**
     * 加载参数库列表
     * @param query
     * @return
     */
    public JSONObject loadGrid(JSONObject query){
        QueryWrapper<Parameter> queryWrapper = new QueryWrapper<>();
        Integer parameterModuleId = query.getInteger("parameterModuleId");
        if (parameterModuleId != null){
            queryWrapper.lambda().eq(Parameter::getParameterModuleId,parameterModuleId);
        }
        Boolean referenced = ruleSetParameterModuleService.isReferenced(Arrays.asList(parameterModuleId));
        List<Parameter> lists = list(queryWrapper);
        List<ParameterVo> parameterVoList = buildParameterVoTree(lists,referenced);
        return JsonUtil.getSuccess("").fluentPut(APP.DATA, parameterVoList);
    }

    /**
     * 构建ParameterVo参数树形结构数据
     * @param dataList  总数据集
     */
    public List<ParameterVo> buildParameterVoTree(List<Parameter> dataList,Boolean referenced) {
        if (dataList == null || dataList.isEmpty()) {
            return new ArrayList<>();
        }
        List<ParameterVo> nodes = new ArrayList<>();
        dataList.forEach(data -> {
            Boolean isAppend = false;
            if (ParameterDataTypeEnum.OBJECT.equals(data.getDataType())){
                isAppend = true;
            }
            if (ParameterDataTypeEnum.ARRAY.equals(data.getDataType())){
                Parameter childParameter = dataList.stream().filter(e ->e.getParentParameterId().equals(data.getParameterId())).findFirst().orElse(null);
                if (childParameter == null){
                    isAppend = true;
                }
            }
            ParameterVo node = new ParameterVo(data.getParameterId(),data.getParentParameterId(),data.getParameterName(),data.getDataType().getTypeId(),data.getDescription(),isAppend,referenced);
            nodes.add(node);
        });
        return toParameterVoTree(nodes);
    }

    /**
     * 构建ParameterVo树形结构数据
     * @param nodes     总数据集
     * @return          树形结构数据集
     */
    public List<ParameterVo> toParameterVoTree(List<ParameterVo> nodes) {
        Map<Integer, List<ParameterVo>> childrenMap = nodes.stream()
                .filter(node -> node.getParentParameterId() != 0)
                .collect(Collectors.groupingBy(ParameterVo::getParentParameterId));
        nodes.forEach(node -> {
            List<ParameterVo> children = childrenMap.get(node.getParameterId());
            if (children != null) {
                node.setChildren(children);
            }
        });
        return nodes.stream().filter(node -> node.getParentParameterId() == 0).collect(Collectors.toList());
    }

    /**
     * 构建ParameterBo树形结构数据
     * @param parameterList
     * @return
     */
    public List<ParameterBo> buildParameterBoTree(List<Parameter> parameterList){
        List<ParameterBo> parameterBoList = new ArrayList<>();
        for (Parameter parameter : parameterList){
            Parameter parentParameter = parameterList.stream().filter(e ->e.getParameterId().equals(parameter.getParentParameterId())).findFirst().orElse(null);
            parameterBoList.add(new ParameterBo(parameter.getParameterId(),parameter.getParentParameterId(),parameter.getParameterName(),parentParameter == null ? null : parentParameter.getParameterName(),parameter.getParameterModelId(),
                    parameter.getParameterModuleId(),parameter.getDataType(),parameter.getDescription()));
        }
        return toParameterBoTree(parameterBoList);
    }

    /**
     * 构建ParameterBo树形结构数据
     * @param parameterBoList     总数据集
     * @return          树形结构数据集
     */
    public List<ParameterBo> toParameterBoTree(List<ParameterBo> parameterBoList) {
        Map<Integer, List<ParameterBo>> childrenMap = parameterBoList.stream()
                .filter(node -> node.getParentParameterId() != 0)
                .collect(Collectors.groupingBy(ParameterBo::getParentParameterId));
        parameterBoList.forEach(node -> {
            List<ParameterBo> children = childrenMap.get(node.getParameterId());
            if (children != null) {
                node.setChildren(children);
            }
        });
        return parameterBoList.stream().filter(node -> node.getParentParameterId() == 0).collect(Collectors.toList());
    }


    /**
     * ParameterBo列表转换成ParameterBo树形结构数据
     * @param parameterBoList     总数据集
     * @return          树形结构数据集
     */
    public List<ParameterBo> transformParameterBoTree(List<ParameterBo> parameterBoList) {
        for (ParameterBo parameterBo : parameterBoList){
            List<String> parameterNameChain = parameterBo.getParameterNameChain();
            int chainLength = parameterNameChain.size();
            for (ParameterBo childParameterBo : parameterBoList){
                if (!parameterBo.equals(childParameterBo)){
                    if (childParameterBo.getParameterNameChain().size() == chainLength + 1){
                        if (parameterNameChain.toString().equals(childParameterBo.getParameterNameChain().subList(0,chainLength).toString())){
                            parameterBo.getChildren().add(childParameterBo);
                        }
                    }
                }
            }
        }
        return parameterBoList.stream().filter(node -> StringUtils.isBlank(node.getParentParameterName())).collect(Collectors.toList());
    }


    /**
     * 根据参数库模型ID，获取参数列表
     * @param parameterModelId
     * @return
     */
    public List<Parameter> getListByModelId(Integer parameterModelId){
        return findByProperty(Parameter::getParameterModelId,parameterModelId);
    }


    /**
     * 根据参数库模块ID，获取参数列表
     * @param parameterModuleId
     * @return
     */
    public List<Parameter> getListByModuleId(Integer parameterModuleId){
        return findByProperty(Parameter::getParameterModuleId,parameterModuleId);
    }

    /**
     * 根据参数库模块ID集合，获取参数列表
     * @param parameterModuleIds
     * @return
     */
    public List<Parameter> getListByModuleIds(List<Integer> parameterModuleIds){
        if (parameterModuleIds == null || parameterModuleIds.isEmpty()){
            return new ArrayList<>();
        }
        QueryWrapper<Parameter> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().in(Parameter::getParameterModuleId,parameterModuleIds);
        return list(queryWrapper);
    }

    /**
     * 根据模块ID和中文名称查询列表
     * @param parentParameterId
     * @param parameterModuleId
     * @param parameterName
     * @return
     */
    public List<Parameter> getListByModuleIdAndName(Integer parentParameterId,Integer parameterModuleId,String parameterName){
        return findByProperties(new String[]{"parentParameterId","parameterModuleId","parameterName"},new Object[]{parentParameterId,parameterModuleId,parameterName});
    }

    /**
     * json数据转成实体
     * @param data  Json数据
     * @return
     */
    public Parameter jsonToPojo(JSONObject data){
        Parameter parameter = new Parameter();
        parameter.setParameterId(data.getInteger("parameterId"));
        parameter.setParentParameterId(data.getInteger("parentParameterId"));
        parameter.setParameterModuleId(data.getInteger("parameterModuleId"));
        parameter.setParameterName(data.getString("parameterName"));
        parameter.setDataType(ParameterDataTypeEnum.typeValueOf(data.getInteger("dataType")));
        parameter.setDescription(data.getString("description"));
        return parameter;
    }

    /**
     * 增加参数
     * @param data
     * @return
     */
    public JSONObject add(JSONObject data){
        JSONObject validateResult = validateModuleData(data);
        if (!validateResult.getBoolean(APP.SUCCESS)){
            return validateResult;
        }
        Parameter parameter = jsonToPojo(data);
        ParameterModule parameterModule = parameterModuleService.findById(parameter.getParameterModuleId());
        if (parameterModule == null){
            return JsonUtil.getFail(ErrorCodeEnum.DATA_NOT_FOUND);
        }
        parameter.setParameterModelId(parameterModule.getParameterModelId());
        save(parameter);
        return JsonUtil.getSuccess("");
    }

    /**
     * 修改参数
     * @param data
     * @return
     */
    public JSONObject update(JSONObject data){
        JSONObject validateResult = validateModuleData(data);
        if (!validateResult.getBoolean(APP.SUCCESS)){
            return validateResult;
        }
        updateById(jsonToPojo(data));
        return JsonUtil.getSuccess("");
    }

    /**
     * 删除参数
     * @param data
     * @return
     */
    public JSONObject delete(JSONObject data) {
        JSONArray ids = data.getJSONArray("ids");
        if (ids != null && !ids.isEmpty()){
            List<Integer> parameterIds = ids.toJavaList(Integer.class);
            List<Parameter> parameterList = findListByIds(parameterIds);
            if (ruleSetParameterModuleService.isReferenced(Arrays.asList(parameterList.get(0).getParameterModuleId()))){
                return JsonUtil.getFail(ErrorCodeEnum.DATA_OPERATION_DENY,"存在被引用的参数,不可删除!");
            }
            removeByIds(parameterIds);
        }
        return JsonUtil.getSuccess("");
    }

    /**
     * 校验数据
     * @param data
     * @return
     */
    public JSONObject validateModuleData(JSONObject data){
        Integer parameterModuleId = data.getInteger("parameterModuleId");
        Integer parentParameterId = data.getInteger("parentParameterId");
        String parameterName =  data.getString("parameterName");
        Integer dataType = data.getInteger("dataType");
        if (StringUtils.isBlank(parameterName) || dataType == null){
            return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM,"缺少参数!");
        }
        Integer parameterId = data.getInteger("parameterId");
        if (parameterId == null){
            if (!getListByModuleIdAndName(parentParameterId,parameterModuleId,parameterName).isEmpty()){
                return JsonUtil.getFail(ErrorCodeEnum.DUPLICATE_DATA,"变量名称重复,请重新输入!");
            }
        }else {
            Parameter oldParameter = findById(parameterId);
            if (oldParameter == null){
                return JsonUtil.getFail(ErrorCodeEnum.DATA_NOT_FOUND,"找不到对应的变量数据!");
            }
            if (!oldParameter.getParameterName().equals(parameterName) && !getListByModuleIdAndName(oldParameter.getParentParameterId(),oldParameter.getParameterModuleId(),parameterName).isEmpty()){
                return JsonUtil.getFail(ErrorCodeEnum.DUPLICATE_DATA,"变量名称重复,请重新输入!");
            }
        }
        return JsonUtil.getSuccess("");
    }


    /**
     * 根据模型ID删除数据
     * @param parameterModelId
     */
    public void deleteByModelId(Integer parameterModelId){
        List<Parameter> parameterList = getListByModelId(parameterModelId);
        if (!parameterList.isEmpty()){
            removeByIds(parameterList.stream().map(Parameter::getParameterId).collect(Collectors.toList()));
        }
    }

    /**
     * 根据模块ID删除数据
     * @param parameterModuleId
     */
    public void deleteByModuleId(Integer parameterModuleId){
        List<Parameter> parameterList = getListByModuleId(parameterModuleId);
        if (!parameterList.isEmpty()){
            removeByIds(parameterList.stream().map(Parameter::getParameterId).collect(Collectors.toList()));
        }
    }

    /**
     * 通过模块列表批量复制参数数据
     * @param fromParameterModuleList
     * @param toParameterModuleList
     * @return
     */
    public void copyDataThroughModuleList(List<ParameterModule> fromParameterModuleList, List<ParameterModule> toParameterModuleList){
        Integer toParameterModelId = toParameterModuleList.get(0).getParameterModelId();
        List<Parameter> parameterList = getListByModuleIds(fromParameterModuleList.stream().map(ParameterModule::getModuleId).collect(Collectors.toList()));
        if (!parameterList.isEmpty()){
            int index;
            for (Parameter parameter : parameterList){
                index = fromParameterModuleList.indexOf(fromParameterModuleList.stream().filter(f -> f.getModuleId().equals(parameter.getParameterModuleId())).findFirst().orElse(null));
                parameter.setParameterModelId(toParameterModelId);
                parameter.setParameterModuleId(toParameterModuleList.get(index).getModuleId());
            }
            Map<Integer,List<Parameter>> groupModelMap = parameterList.stream().collect(Collectors.groupingBy(Parameter::getParameterModelId));
            groupModelMap.forEach((key,value)->{
                if (!value.isEmpty()){
                    List<ParameterBo> parameterBoList = buildParameterBoTree(value);
                    saveParameterLoop(0,parameterBoList);
                }
            });
        }
    }

    /**
     * 通过模块ID批量复制参数数据
     * @param fromParameterModuleId
     * @param toParameterModuleId
     * @return
     */
    public void copyDataThroughModuleId(Integer fromParameterModuleId, Integer toParameterModuleId){
        List<Parameter> parameterList = getListByModuleId(fromParameterModuleId);
        if (!parameterList.isEmpty()){
            parameterList.forEach(e ->e.setParameterModuleId(toParameterModuleId));
            List<ParameterBo> parameterBoList = buildParameterBoTree(parameterList);
            saveParameterLoop(0,parameterBoList);
        }
    }

    /**
     * 通过模型ID导出参数数据
     * @param parameterModelId
     * @param response
     */
    public JSONObject exportByModelId(Integer parameterModelId, HttpServletResponse response){
        ParameterModel parameterModel = parameterModelService.findById(parameterModelId);
        if (parameterModel == null){
            return JsonUtil.getFail(ErrorCodeEnum.DATA_NOT_FOUND);
        }
        List<ParameterModule> parameterModuleList = parameterModuleService.getListByModelId(parameterModelId);
        if (parameterModuleList.isEmpty()){
            return JsonUtil.getFail(ErrorCodeEnum.DATA_NOT_FOUND);
        }
        QueryWrapper<Parameter> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(Parameter::getParameterModelId,parameterModelId);
        queryWrapper.lambda().orderByAsc(Parameter::getParameterModuleId);
        List<Parameter> parameterList = list(queryWrapper);
        if (parameterList.isEmpty()){
            return JsonUtil.getFail(ErrorCodeEnum.DATA_NOT_FOUND);
        }
        return export(parameterModel,parameterModuleList,parameterList,response);
    }

    /**
     * 通过模块ID导出参数数据
     * @param parameterModuleId
     * @param response
     */
    public JSONObject exportByModuleId(Integer parameterModuleId, HttpServletResponse response){
        ParameterModule parameterModule = parameterModuleService.findById(parameterModuleId);
        if (parameterModule == null){
            return JsonUtil.getFail(ErrorCodeEnum.DATA_NOT_FOUND);
        }
        ParameterModel parameterModel = parameterModelService.findById(parameterModule.getParameterModelId());
        if (parameterModel == null){
            return JsonUtil.getFail(ErrorCodeEnum.DATA_NOT_FOUND);
        }
        List<Parameter> parameterList = getListByModuleId(parameterModuleId);
        if (parameterList.isEmpty()){
            return JsonUtil.getFail(ErrorCodeEnum.DATA_NOT_FOUND);
        }
        return export(parameterModel, Arrays.asList(parameterModule),parameterList,response);
    }

    /**
     * 导出参数数据
     * @param parameterModel
     * @param parameterModuleList
     * @param parameterList
     * @param response
     * @return
     */
    public JSONObject export(ParameterModel parameterModel,List<ParameterModule> parameterModuleList,List<Parameter> parameterList, HttpServletResponse response){
        HSSFWorkbook workbook = new HSSFWorkbook();
        String sheetPrefixName = parameterModel.getPropertyName() + "参数库-";
        String sheetSuffixName = parameterModuleList.size() == 1 ? parameterModuleList.get(0).getPropertyName() + "参数集-" : "";
        String sheetName = sheetPrefixName + sheetSuffixName + "参数列表";
        try (OutputStream os = response.getOutputStream()) {
            response.setContentType("application/octet-stream");
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
            response.setHeader("Content-Disposition", "attachment;filename=" + new String(sheetName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
            response.addHeader("Cache-Control", "no-cache");
            workbook.write(os);
            os.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return JsonUtil.getSuccess("");
    }

    /**
     * 模板文件
     */
    @Value("classpath:templates/import_template_parameter.xls")
    private Resource template;

    /**
     * 保存参数数据
     * @param parentParameterId
     * @param parameterBoList
     */
    public void saveParameterLoop(Integer parentParameterId,List<ParameterBo> parameterBoList){
        for (ParameterBo parameterBo : parameterBoList){
            Parameter parameter = ParameterBoToPojo(parentParameterId,parameterBo);
            parameter.setParameterId(null);
            save(parameter);
            if (!parameterBo.getChildren().isEmpty()){
                saveParameterLoop(parameter.getParameterId(),parameterBo.getChildren());
            }
        }
    }

    /**
     * parameterBo实体转换
     * @param parentParameterId
     * @param parameterBo
     * @return
     */
    public Parameter ParameterBoToPojo(Integer parentParameterId,ParameterBo parameterBo){
        return new Parameter(parameterBo.getParameterId(),parentParameterId,parameterBo.getParameterModelId(),parameterBo.getParameterModuleId(),parameterBo.getParameterName(),parameterBo.getDataType(),parameterBo.getDescription());
    }

    /**
     * 根据参数模块id列表获取参数列表
     * @param parameterModuleIdList
     * @return
     */
    public List<Parameter> getListByModuleIdList(List<Integer> parameterModuleIdList){
        if(parameterModuleIdList.isEmpty()){
            return new ArrayList<>();
        }
        LambdaQueryWrapper<Parameter> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.in(Parameter::getParameterModuleId,parameterModuleIdList);
        return list(lambdaQueryWrapper);
    }


    /**
     * 加载数据类型配置列表
     */
    public JSONObject loadDataTypeOptionList(){
        List<ParameterDataTypeEnum> dataTypeEnumList = Arrays.asList(ParameterDataTypeEnum.values());
        JSONArray dataList = new JSONArray();
        for(ParameterDataTypeEnum dataTypeEnum : dataTypeEnumList){
            JSONObject data = new JSONObject();
            data.fluentPut("value",dataTypeEnum.getTypeId())
                    .fluentPut("label", dataTypeEnum.getTypeName());
            dataList.add(data);
        }
        return JsonUtil.getSuccess("").fluentPut(APP.DATA,dataList);
    }
}
